package com.hqq.stack;

import com.hqq.stack.exception.StackEmptyException;
import com.hqq.stack.exception.StackFullException;

import java.util.Iterator;

/**
 * ArrayStack
 * 内部使用数组实现
 * Created by heqianqian on 2017/8/3.
 */
public class ArrayStack<T> implements Stack<T> {

    private static final int DEAULT_SIZE = 16;

    private T[] array;

    private int count;

    private int size;

    @SuppressWarnings("unchecked")
    public ArrayStack() {
        this.size = DEAULT_SIZE;
        array = (T[]) new Object[size];
    }

    @SuppressWarnings("unchecked")
    public ArrayStack(int size) {
        this.size = size;
        array = (T[]) new Object[size];
    }

    @Override
    public void push(T t) throws StackFullException {
        if (count == size) {//数组已满进行扩容
            expand();
        }
        array[count++] = t;
    }

    @Override
    public T pop() throws StackEmptyException {
        if (count > 0) {
            T result = array[--count];
            array[count] = null;//方便gc回收
            return result;
        }
        throw new StackEmptyException("Stack is Empty");
    }

    @Override
    public T top() {
        return array[count - 1];
    }

    @Override
    public boolean isEmpty() {
        return count == 0;
    }

    @Override
    public int size() {
        return count;
    }

    public T get(int index) {
        if (index < 0 || index > count) {
            return null;
        }
        return array[index];
    }

    /**
     * 数组扩容 默认扩展当前数组的一倍
     */
    @SuppressWarnings("unchecked")
    private void expand() {
        T[] newArray = (T[]) new Object[size << 1];
        System.arraycopy(array, 0, newArray, 0, size);
        array = newArray;
    }

    @Override
    public Iterator<T> iterator() {
        return new ArrayStackIterator<>(this);
    }

    /**
     * 迭代器
     */
    private class ArrayStackIterator<T> implements Iterator<T> {

        private ArrayStack<T> stack;

        private int index;

        public ArrayStackIterator(ArrayStack<T> stack) {
            this.stack = stack;
            index = stack.size() - 1;
        }

        @Override
        public boolean hasNext() {
            return index > 0;
        }

        @Override
        public T next() {
            return stack.get(index--);
        }
    }
}
